/*!
 * @file       main.c
 *
 * @brief      ETH Main program body
 *
 * @version    V1.0.0
 *
 * @date       2022-05-25
 *
 */

#include "main.h"
#include "Board.h"
#include "bsp_lcd.h"
#include "bsp_lcdFont.h"

/** printf function configs to USART1*/
#define DEBUG_USART  USART1

/** Global pointers on Rx descriptor used to transmit and receive descriptors */
extern ETH_DMADescConfig_T  *DMARxDescToGet;

/** current Ethernet LocalTime value */
volatile uint32_t ETHTimer = 0;
/** lwip network interface structure for ethernetif */
struct netif UserNetif;

/** TCP periodic Timer */
uint32_t TCPTimer = 0;
/** ARP periodic Timer */
uint32_t ARPTimer = 0;
/** MAC address */
uint8_t SetMACaddr[6] = {0,0,0,0,0,8};

/** Configure SysTick */
void ConfigSysTick(void);
/** User config the different system Clock */
void UserRCMClockConfig(void);
/** Configures COM port */
void APM_BOARD_COMInit(COM_TypeDef COM, USART_Config_T* configStruct);

/*!
 * @brief       Main program
 *
 * @param       None
 *
 * @retval      None
 */
int main(void)
{
    USART_Config_T usartConfig;

    /** User config the different system Clock */
    UserRCMClockConfig();

    /** Configure SysTick */
    ConfigSysTick();

    /** Configure USART */
    usartConfig.baudRate = 115200;
    usartConfig.wordLength = USART_WORD_LEN_8B;
    usartConfig.stopBits = USART_STOP_BIT_1;
    usartConfig.parity = USART_PARITY_NONE ;
    usartConfig.mode = USART_MODE_TX_RX;
    usartConfig.hardwareFlow = USART_HARDWARE_FLOW_NONE;

    APM_BOARD_COMInit(COM1,&usartConfig);

    LCD_GPIO_Init();
    LCD_SMC_Init();
    LCD_Reset();
    LCD_ConfigRegister();
    LCD_EnableBackLight();

    LCD_ConfigScan(LCD_SCAN_MODE_6);
    LCD_ClearScreen();

    LCD_PrintString(30, 40, "This is a ETH Demo!", LCD_COLOR_USER);

    /** Configure ethernet (GPIOs, clocks, MAC, DMA) */
    ConfigEthernet();

    /** Initilaize the LwIP stack */
    LwIP_Init();

    LCD_PrintString(30, 80, "  Static IP address  ", LCD_COLOR_USER);
    LCD_PrintString(30, 120, "IP: 192,168,73,22\n", LCD_COLOR_USER);
    LCD_PrintString(30, 160, "NETMASK: 255,255,255,0\n", LCD_COLOR_USER);
    LCD_PrintString(30, 200, "Gateway: 192,168,73,1\n", LCD_COLOR_USER);

    while(1)
    {
        /** check if any packet received */
        if (ETH_ReadRxPacketSize(DMARxDescToGet))
        {
            /** process received ethernet packet */
            LwIP_Pkt_Handle();
        }
            /** handle periodic timers for LwIP */
        LwIP_Periodic_Handle(ETHTimer);
    }
}

 /*!
 * @brief       Configure SysTick
 *
 * @param       None
 *
 * @retval      None
 */
 void ConfigSysTick(void)
{
    if(SysTick_Config(SystemCoreClock/100))
    {
        /** Capture error */
        while (1);
    }
}

/*!
 * @brief       This function initializes the lwIP stack
 *
 * @param       None
 *
 * @retval      None
 */
void LwIP_Init(void)
{
    struct ip_addr ipaddr;
    struct ip_addr netmask;
    struct ip_addr gw;

    /** Initializes the dynamic memory heap */
    mem_init();

    /** Initializes the memory pools */
    memp_init();

    IP4_ADDR(&ipaddr, 192, 168, 73, 22);
    IP4_ADDR(&netmask, 255, 255 , 255, 0);
    IP4_ADDR(&gw, 192, 168, 73, 1);

    /** Config MAC Address */
    ETH_ConfigMACAddress(ETH_MAC_ADDRESS0, SetMACaddr);

    /** Add a network interface to the list of lwIP netifs */
    netif_add(&UserNetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);

    /** Registers the default network interface */
    netif_set_default(&UserNetif);

    /** When the netif is fully configured this function must be called */
    netif_set_up(&UserNetif);

    /** Use Com printf static IP address*/
    printf("\n  Static IP address   \n");
    printf("IP: %d.%d.%d.%d\n",192,168,73,22);
    printf("NETMASK: %d.%d.%d.%d\n",255,255,255,0);
    printf("Gateway: %d.%d.%d.%d\n",192,168,73,1);
}

/*!
 * @brief       This function received ethernet packet
 *
 * @param       None
 *
 * @retval      None
 */
void LwIP_Pkt_Handle(void)
{
  /** Read a received packet from the Ethernet buffers and send it to the lwIP for handling */
  ethernetif_input(&UserNetif);
}

/*!
 * @brief       This function LwIP periodic tasks
 *
 * @param       ETHTimer the current Ethernet Timer value
 *
 * @retval      None
 */
void LwIP_Periodic_Handle(__IO uint32_t ETHTimer)
{
#if LWIP_TCP
    /** TCP periodic process every 250 ms */
    if (ETHTimer - TCPTimer >= TCP_TMR_INTERVAL)
    {
        TCPTimer =  ETHTimer;
        tcp_tmr();
    }
#endif

    /** ARP periodic process every 5s */
    if ((ETHTimer - ARPTimer) >= ARP_TMR_INTERVAL)
    {
        ARPTimer =  ETHTimer;
        etharp_tmr();
    }
}

/*!
 * @brief       User config the different system Clock
 *
 * @param       None
 *
 * @retval      None
 */
void UserRCMClockConfig(void)
{
    RCM_Reset();
    RCM_ConfigHSE(RCM_HSE_OPEN);

    if(RCM_WaitHSEReady() == SUCCESS)
    {
        RCM_ConfigPLL1(RCM_PLLSEL_HSE,8,200,RCM_PLL_SYS_DIV_2,5);
    }
    RCM_EnablePLL1();

    while(RCM_ReadStatusFlag(RCM_FLAG_PLL1RDY) == RESET)
    {
        ;
    }

    RCM_ConfigSYSCLK(RCM_SYSCLK_SEL_PLL);

    while(RCM_ReadSYSCLKSource()!= RCM_SYSCLK_SEL_PLL)
    {
        ;
    }
}

/*!
 * @brief       Configures COM port.
 *
 * @param       COM: Specifies the COM port to be configured.
 *              This parameter can be one of following parameters:
 *              @arg COM1
 *              @arg COM2
 *
 * @retval      None
 */
void APM_BOARD_COMInit(COM_TypeDef COM, USART_Config_T* configStruct)
{
    #if defined (APM32F407_MINI)
    APM_MINI_COMInit(COM1,configStruct);
    #elif defined (APM32F407_ELE_HUETB)
    APM_ELE_HUETB_COMInit(COM1,configStruct);
    #elif defined (APM32F407_EVAL)
    APM_EVAL_COMInit(COM1,configStruct);
    #else
    #error "Please select first the APM32  board to be used (in board.h)"
    #endif
}

/*!
 * @brief       Redirect C Library function printf to serial port.
 *              After Redirection, you can use printf function.
 *
 * @param       ch:  The characters that need to be send.
 *
 * @param       *f:  pointer to a FILE that can recording all information
 *              needed to control a stream
 *
 * @retval      The characters that need to be send.
 */
int fputc(int ch, FILE* f)
{
    /** send a byte of data to the serial port */
    USART_TxData(DEBUG_USART, (uint8_t)ch);

    /** wait for the data to be send  */
    while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);

    return (ch);
}
